home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / System 7.0 Samples / INIT - CDEV / INIT.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-18  |  13.1 KB  |  390 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. *
  3. *    Macintosh Developer Technical Support
  4. *
  5. *    Sample Control Panel Device and INIT Combination
  6. *
  7. *    Program:    INIT - CDEV
  8. *    File:        INIT.c    -    C Source
  9. *
  10. *    Copyright © 1990 Apple Computer, Inc.
  11. *    All rights reserved.
  12. *
  13. *-----------------------------------------------------------------------------*/
  14.  
  15. #include <Common.h>
  16. #include <SAGlobals.h>
  17. #include <Processes.h>
  18.  
  19.  
  20. /*------------------------------------------------------------------------------
  21.     Global variables
  22. ------------------------------------------------------------------------------*/
  23.  
  24. CommonGlobalsRec    gCommonGlobals;        /* Globals common to INIT and CDEV    */
  25. SessionRecord        gSessionRecord;        /* Data for PPC communications        */
  26. long                gTimeLastBeeped;    /* Tickcount when we last beeped    */
  27.  
  28.  
  29. /*------------------------------------------------------------------------------
  30.     Procedure Prototypes and External Routines
  31. ------------------------------------------------------------------------------*/
  32.  
  33.         OSErr    c_Install(void);
  34.         OSErr    DoPPCInit(void);
  35.         OSErr    ReadPreferences(void);
  36.         void    c_SystemTask(void);
  37.         OSErr    OpenAPort(void);
  38.  
  39. pascal    void    InformCompProc(PPCParamBlockPtr ppb);
  40. pascal    void    WriteCompProc(PPCParamBlockPtr ppb);
  41. pascal    void    EndCompProc(PPCParamBlockPtr ppb);
  42.  
  43. extern    void        a_SetA5Ref(A5RefType);
  44. extern    A5RefType    a_GetA5Ref(void);
  45.  
  46.  
  47. /*------------------------------------------------------------------------------
  48.  
  49.     OSErr c_Install(void)
  50.  
  51.     This is the C portion of the installation process. It is called from the
  52.     assembly installation code at INIT time.
  53.  
  54.     We're going to be making all of our memory allocation from the System
  55.     zone. There's really no other choice. We're an INIT; we don't have our own
  56.     zone to get memory from. So we save off whatever the current zone is
  57.     (which very well may be the System Zone -- what do I know?), and set the
  58.     zone to the System Zone.
  59.  
  60.     Next, we get the memory for our globals. We call MakeA5World to do this,
  61.     which is a modification of the routine described in Technote #256. If we
  62.     don't get our memory, restore the zone and return to our caller with an
  63.     "out of memory" error. If we do get the memory, then save off the old A5
  64.     and set A5 to be our A5. Also, save our A5 reference so that when our
  65.     SystemTask patch runs, it knows what value to use for A5.
  66.  
  67.     After that, we do some more initialization specific to the function of
  68.     this INIT (which is to beep, annoying the **** out of the user). We call
  69.     two subroutines for this, which do PPCToolbox setup, and read our
  70.     preferences file.
  71.  
  72.     Finally, we clean up and leave. If any errors occured, we gett rid of the
  73.     buffer for our globals. We then restore A5 and the zone, and return any
  74.     error numbers.
  75.  
  76. ------------------------------------------------------------------------------*/
  77.  
  78. OSErr c_Install(void)
  79. {
  80.     OSErr        err;                /* For any routines that return errors */
  81.     THz            oldZone;            /* Holds the current zone while we switch
  82.                                        over to the System zone for our memory
  83.                                        allocation. */
  84.     A5RefType    A5Ref;                /* The reference to our globals. This is
  85.                                        created by the SAGlobals package. */
  86.     long        oldA5;                /* Holds the current A5 when we switch
  87.                                        over to the A5 for our own globals. */
  88.  
  89.     err = noErr;
  90.     oldZone = GetZone();
  91.     SetZone(SystemZone());
  92.  
  93.     MakeA5World(&A5Ref);
  94.     if (A5Ref == nil) {
  95.         SetZone(oldZone);
  96.         return(memFullErr);
  97.     }
  98.     a_SetA5Ref(A5Ref);
  99.     oldA5 = SetA5World(A5Ref);
  100.  
  101.     (void) DoPPCInit();
  102.     (void) ReadPreferences();
  103.  
  104.     (void) SetA5(oldA5);
  105.     SetZone(oldZone);
  106.     return (err);
  107. }
  108.  
  109.  
  110. /*------------------------------------------------------------------------------
  111.  
  112.     OSErr    DoPPCInit(void)
  113.  
  114.     Check to see if we have PPCToolbox facilities. If so, initialize the
  115.     PPCToolbox, open a port, and perform a PPCInform call on the port (this is
  116.     done with the EndCompProc procedure down below).
  117.  
  118.     If there are any errors, we set "err" to the result code. However, we have
  119.     to distinguish between hard and soft errors. If the PPCToolbox is not
  120.     around, that is a soft error, so we make sure to clear "err" if Gestalt
  121.     says PPCToolbox is not there. Likewise, if the PPCInform call reports a
  122.     result of "1", that means the asynchronous call is pending, and is also a
  123.     soft error, so we also clear "err" in that case. Otherwise, any errors are
  124.     hard errors, and indicate some serious problems. In that case, we report
  125.     the error to our caller so that it can abort the installation of the INIT.
  126.  
  127. ------------------------------------------------------------------------------*/
  128.  
  129. OSErr    DoPPCInit(void)
  130. {
  131.     OSErr        err;                /* For any routines that return errors */
  132.     long        gestaltResult;        /* Gestalt result. duh! */
  133.  
  134.     err = Gestalt(gestaltPPCToolboxAttr, &gestaltResult);
  135.     if (!err) {
  136.         err = PPCInit();
  137.         if (!err) {
  138.             err = OpenAPort();
  139.             if (!err) {
  140.                 EndCompProc(&gSessionRecord.pb);
  141.                 err = gSessionRecord.pb.informParam.ioResult;
  142.                 if (err > 0) {
  143.                     err = noErr;                /* cover up for soft errors */
  144.                 }
  145.             }
  146.         }
  147.     } else
  148.         err = noErr;                            /* cover up for soft errors */
  149.  
  150.     return (err);
  151. }
  152.  
  153.  
  154. /*------------------------------------------------------------------------------
  155.  
  156.     OSErr    ReadPreferences(void)
  157.  
  158.     See if we can read the preferences file. First, we see if the FindFolder
  159.     routine is present with a call to Gestalt. If so, we look for the
  160.     preferences file. If one exists, we open it up, and read the values into
  161.     our public globals buffer (the preferences file is just an image of that
  162.     buffer, so the values read right in).
  163.  
  164.     If the FindFolder routine doesn't exist on this machine, or the
  165.     preferences file doesn't exists, or there was an error when reading the
  166.     file, we go right ahead and use a set of hard-wired values. All errors  at
  167.     this stage of the INIT are considered soft errors, so we clear "err".
  168.  
  169.     Finally, we set "gTimeLastBeeped" to be the current tickcount.
  170.  
  171. ------------------------------------------------------------------------------*/
  172.  
  173. OSErr    ReadPreferences(void)
  174. {
  175.     OSErr        err;                /* For any routines that return errors */
  176.     long        gestaltResult;        /* Gestalt result. duh! */
  177.     Boolean        useDefaults;        /* Boolean that we use to determine if we
  178.                                        were able to read the preferences file.
  179.                                        If not, this is TRUE, and we use default
  180.                                        values instead. */
  181.     FSSpec        spec;                /* FSSpec for reading our preferences */
  182.     short        refNum;                /* Refnum for our preferences file */
  183.     long        amountToRead;        /* Number of bytes to read from pref. */
  184.  
  185.     useDefaults = true;
  186.     err = Gestalt(gestaltFindFolderAttr, &gestaltResult);
  187.     if ((err == noErr) && (gestaltResult != 0)) {
  188.         err = FindFolder(kOnSystemDisk, kPreferencesFolderType,
  189.                         kCreateFolder, &spec.vRefNum, &spec.parID);
  190.         if (!err) {
  191.             (void) PLstrcpy(spec.name, kPrefsFileName);
  192.             err = FSpOpenDF(&spec, fsRdPerm, &refNum);
  193.             if (!err) {
  194.                 amountToRead = sizeof(gCommonGlobals);
  195.                 err = FSRead(refNum, &amountToRead, (Ptr) &gCommonGlobals);
  196.                 if (!err) {
  197.                     err = FSClose(refNum);
  198.                     useDefaults = false;
  199.                 }
  200.             }
  201.         }
  202.     }
  203.  
  204.     err = noErr;
  205.  
  206.     if (useDefaults) {
  207.         gCommonGlobals.timesToBeep = 3;
  208.         gCommonGlobals.beepInterval = 60*60;    /* start off once a min. */
  209.     }
  210.     gTimeLastBeeped = TickCount();
  211.  
  212.     return(err);
  213. }
  214.  
  215.  
  216. /*------------------------------------------------------------------------------
  217.  
  218.     void c_SystemTask(void)
  219.  
  220.     This routine is the meat of the SystemTask() patch. It is called from
  221.     a_SystemTask, an assembly routine that saves the registers, calls us,
  222.     restores the registers, and then calls the real SystemTask() in a way that
  223.     doesn't constitute a tail patch.
  224.  
  225.     All we do here is see if an appropriate amount of time has passed. If so,
  226.     we beep a certain number of times. Otherwise, we do nothing.
  227.  
  228. ------------------------------------------------------------------------------*/
  229.  
  230. void c_SystemTask(void)
  231. {
  232.     long                oldA5;
  233.     short                loopy;
  234.  
  235.     oldA5 = SetA5World(a_GetA5Ref());
  236.  
  237.     if (TickCount() >= gTimeLastBeeped + gCommonGlobals.beepInterval) {
  238.         for (loopy = 0; loopy < gCommonGlobals.timesToBeep; ++loopy) {
  239.             SysBeep(5);
  240.         }
  241.         gTimeLastBeeped = TickCount();
  242.     }
  243.  
  244.     (void) SetA5(oldA5);
  245. }
  246.  
  247.  
  248. /*------------------------------------------------------------------------------
  249.  
  250.     OSErr OpenAPort(void)
  251.  
  252.     Used to open a PPC port. We identify ourselves using the portName record.
  253.     First, we give ourselves a name that will show up in the PPCBrowser
  254.     (portName.name). We then give ourselves an identify to other processes. We
  255.     can do this either by name or by creator/type signatures. In our case, we
  256.     choose creator/type ('INCD'/'INIT').
  257.  
  258.     We then set up for a PPCOpen call. We are making a synchronous call, so we
  259.     don't need a completion routine. ServiceType and resFlag are set to
  260.     required values (per Inside Mac). We make the INIT not visible over the
  261.     network. However, if we did, it's possible to talk to it from another
  262.     Macintosh. If the other Mac know our communication protocol, it could
  263.     change our beep parameters out from under us. Next, we point to the name
  264.     record we want to use for our port, and use the default location name
  265.     (which identifies our computer to other computers on the network).
  266.  
  267.     Finally, we make the PPCOpen call synchronously, and return any errors.
  268.  
  269. ------------------------------------------------------------------------------*/
  270.  
  271. OSErr OpenAPort(void)
  272. {
  273.     gSessionRecord.portName.nameScript = GetScriptManagerVariable(smSysScript);
  274.     (void) PLstrcpy(gSessionRecord.portName.name, "\pBG Beeper");
  275.     gSessionRecord.portName.portKindSelector = ppcByCreatorAndType;
  276. #if GENERATING68K
  277.     // Universal Interfaces 2.0
  278.     gSessionRecord.portName.u.port.portCreator = kCreator;
  279.     gSessionRecord.portName.u.port.portType = 'INIT';
  280. #else
  281.     gSessionRecord.portName.u.port.creator = kCreator;
  282.     gSessionRecord.portName.u.port.type = 'INIT';
  283. #endif
  284.  
  285.     gSessionRecord.pb.openParam.ioCompletion = nil;
  286.     gSessionRecord.pb.openParam.serviceType = ppcServiceRealTime;
  287.     gSessionRecord.pb.openParam.resFlag = 0;
  288.     gSessionRecord.pb.openParam.networkVisible = false;
  289.     gSessionRecord.pb.openParam.portName = &gSessionRecord.portName;
  290.     gSessionRecord.pb.openParam.locationName = nil;    // use the default location
  291.  
  292.     return(PPCOpen(&gSessionRecord.pb.openParam, false));
  293. }
  294.  
  295.  
  296. /*------------------------------------------------------------------------------
  297.  
  298.     pascal void InformCompProc(PPCParamBlockPtr ppb)
  299.  
  300.     After we open a PPC port, we make an asynchronous PPCInform call on it.
  301.     When someone wants to talk to us, the completion routine for the PPCInform
  302.     call gets called. That's this routine. When we get called, it means the
  303.     CDEV wants to talk to us. We find out what it's asking by looking at the
  304.     "userData" field, in which the CDEV has placed a message number. We only
  305.     know kGetCommonGlobalsPtr, so we respond to that by returning the pointer
  306.     to our public globals. After filling out the parameter block and i/o
  307.     buffer, we make a PPCWrite call. We are called at interrupt time (we are a
  308.     completion routine, remember), so we make the PPCWrite call
  309.     asynchronously. We provide a completion routine that gets executed when
  310.     the call completes.
  311.  
  312.     Because this is a completion routine, it is declared as using the Pascal
  313.     calling convention.
  314.  
  315. ------------------------------------------------------------------------------*/
  316.  
  317. pascal void InformCompProc(PPCParamBlockPtr ppb)
  318. {
  319.     OSErr                err;
  320.     GetCommonGlobalsPtr    myBuffer;
  321.  
  322.     myBuffer = (GetCommonGlobalsPtr) ((SessionPtr)ppb)->buffer;
  323.  
  324.     ppb->writeParam.ioCompletion    = (PPCCompProcPtr) WriteCompProc;
  325.     ppb->writeParam.bufferLength    = 0;
  326.     ppb->writeParam.bufferPtr        = (Ptr) myBuffer;
  327.     ppb->writeParam.more            = false;
  328.  
  329.     switch (ppb->informParam.userData) {
  330.         case kGetCommonGlobalsPtr:
  331.             ppb->writeParam.bufferLength    = sizeof(GetCommonGlobalsRecord);
  332.             myBuffer->commonGlobalsAddress    = &gCommonGlobals;
  333.             break;
  334.     }
  335.  
  336.     err = PPCWriteAsync(&ppb->writeParam);
  337. }
  338.  
  339.  
  340. /*------------------------------------------------------------------------------
  341.  
  342.     pascal void WriteCompProc(PPCParamBlockPtr ppb)
  343.  
  344.     When our PPCWrite routine completes, the PPCToolbox calls this completion
  345.     routine. All this is responsible for it closing the session on this end by
  346.     calling PPCEnd.
  347.  
  348.     Because this is a completion routine, it is declared as using the Pascal
  349.     calling convention.
  350.  
  351. ------------------------------------------------------------------------------*/
  352.  
  353. pascal void WriteCompProc(PPCParamBlockPtr ppb)
  354. {
  355.     OSErr            err;
  356.  
  357.     ppb->endParam.ioCompletion = (PPCCompProcPtr) EndCompProc;
  358.  
  359.     err = PPCEndAsync(&ppb->endParam);
  360. }
  361.  
  362.  
  363. /*------------------------------------------------------------------------------
  364.  
  365.     pascal void EndCompProc(PPCParamBlockPtr ppb)
  366.  
  367.     After the PPCEnd call completes, the PPCToolbox calls this completion
  368.     routine. This routine is responsible for setting us up for receiving more
  369.     PPC communications requestions. We also call this routine when we want to
  370.     set ourselves for the very first PPCInform call.
  371.  
  372.     Because this is a completion routine, it is declared as using the Pascal
  373.     calling convention.
  374.  
  375. ------------------------------------------------------------------------------*/
  376.  
  377. pascal void EndCompProc(PPCParamBlockPtr ppb)
  378. {
  379.     OSErr            err;
  380.  
  381.     ppb->informParam.ioCompletion    = (PPCCompProcPtr) InformCompProc;
  382.     ppb->informParam.autoAccept        = true;
  383.     ppb->informParam.portName        = &((SessionPtr)ppb)->portName;
  384.     ppb->informParam.locationName    = &((SessionPtr)ppb)->locationName;
  385.     ppb->informParam.userName        = &((SessionPtr)ppb)->userName;
  386.  
  387.     err = PPCInformAsync(&ppb->informParam);
  388. }
  389.  
  390.